{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#协同过滤推荐算法简单代码实现（基于用户、jaccard）"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "from pprint import pprint"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "       ItemA  ItemB  ItemC  ItemD  ItemE\n",
      "User1      1      0      1      1      0\n",
      "User2      1      0      0      1      1\n",
      "User3      1      0      1      0      0\n",
      "User4      0      1      0      1      1\n",
      "User5      1      1      1      0      1\n",
      "Index(['User1', 'User2', 'User3', 'User4', 'User5'], dtype='object')\n",
      "Index(['ItemA', 'ItemB', 'ItemC', 'ItemD', 'ItemE'], dtype='object')\n"
     ]
    }
   ],
   "source": [
    "#构建用户购买记录数据集（1买了，0没买）\n",
    "users = [\"User1\",\"User2\",\"User3\",\"User4\",\"User5\",]\n",
    "items = [\"ItemA\",\"ItemB\",\"ItemC\",\"ItemD\",\"ItemE\"]\n",
    "datasets = [\n",
    "    [1,0,1,1,0],\n",
    "    [1,0,0,1,1],\n",
    "    [1,0,1,0,0],\n",
    "    [0,1,0,1,1],\n",
    "    [1,1,1,0,1],\n",
    "]\n",
    "df = pd.DataFrame(datasets,columns=items,index=users)\n",
    "print(df)\n",
    "print(df.index)\n",
    "print(df.colum)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.2\n",
      "0.5\n"
     ]
    }
   ],
   "source": [
    "#直接计算某两项的杰卡德相似系数 （两项的交集/两项的并集）\n",
    "from sklearn.metrics import jaccard_score\n",
    "print(jaccard_score(df['ItemA'],df['ItemB'])) #1/5\n",
    "print(jaccard_score(df.loc['User1'],df.loc['User2'])) #2/4"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "用户之间的相似度\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.7/site-packages/sklearn/metrics/pairwise.py:1761: DataConversionWarning: Data was converted to boolean for metric jaccard\n",
      "  warnings.warn(msg, DataConversionWarning)\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>User1</th>\n",
       "      <th>User2</th>\n",
       "      <th>User3</th>\n",
       "      <th>User4</th>\n",
       "      <th>User5</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>User1</th>\n",
       "      <td>1.000000</td>\n",
       "      <td>0.50</td>\n",
       "      <td>0.666667</td>\n",
       "      <td>0.2</td>\n",
       "      <td>0.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>User2</th>\n",
       "      <td>0.500000</td>\n",
       "      <td>1.00</td>\n",
       "      <td>0.250000</td>\n",
       "      <td>0.5</td>\n",
       "      <td>0.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>User3</th>\n",
       "      <td>0.666667</td>\n",
       "      <td>0.25</td>\n",
       "      <td>1.000000</td>\n",
       "      <td>0.0</td>\n",
       "      <td>0.5</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>User4</th>\n",
       "      <td>0.200000</td>\n",
       "      <td>0.50</td>\n",
       "      <td>0.000000</td>\n",
       "      <td>1.0</td>\n",
       "      <td>0.4</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>User5</th>\n",
       "      <td>0.400000</td>\n",
       "      <td>0.40</td>\n",
       "      <td>0.500000</td>\n",
       "      <td>0.4</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "          User1  User2     User3  User4  User5\n",
       "User1  1.000000   0.50  0.666667    0.2    0.4\n",
       "User2  0.500000   1.00  0.250000    0.5    0.4\n",
       "User3  0.666667   0.25  1.000000    0.0    0.5\n",
       "User4  0.200000   0.50  0.000000    1.0    0.4\n",
       "User5  0.400000   0.40  0.500000    0.4    1.0"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#计算所有数据两两之间的杰卡德相似系数\n",
    "from sklearn.metrics.pairwise import pairwise_distances\n",
    "# 计算用户间相似度(相似度 = 1-杰卡德距离) 默认是行\n",
    "user_similar = 1 - pairwise_distances(df.values,metric='jaccard')\n",
    "user_similar = pd.DataFrame(user_similar,columns=users,index=users)\n",
    "print(\"用户之间的相似度\")\n",
    "user_similar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "每个用户最相似的两个用户\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'User1': ['User3', 'User2'],\n",
       " 'User2': ['User4', 'User1'],\n",
       " 'User3': ['User1', 'User5'],\n",
       " 'User4': ['User2', 'User5'],\n",
       " 'User5': ['User3', 'User4']}"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#为每个用户找到最相似的K个用户（k=2）\n",
    "topN_users = {}\n",
    "for i in user_similar.index:\n",
    "    _df = user_similar.loc[i].drop(i)#取出第i个用户的那一行，删除自身（自己与自己的相似度为1）\n",
    "    _df_sorted = _df.sort_values(ascending = False)  #降序排列\n",
    "    top2 = list(_df_sorted.index[:2])#找到前两个\n",
    "    topN_users[i] = top2\n",
    "    \n",
    "print(\"每个用户最相似的两个用户\")\n",
    "topN_users"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "基于用户推荐\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "{'User1': {'ItemE'},\n",
       " 'User2': {'ItemB', 'ItemC'},\n",
       " 'User3': {'ItemB', 'ItemD', 'ItemE'},\n",
       " 'User4': {'ItemA', 'ItemC'},\n",
       " 'User5': {'ItemD'}}"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "rs_users = {}\n",
    "for user,sim_users in topN_users.items():\n",
    "    rs_user = set()# 每个用户都有一个推荐结果\n",
    "    for sim_user in sim_users:#和该用户相似的用户都买过什么，放在一起，set去重\n",
    "        rs_user = rs_user.union(set(df.loc[sim_user].replace(0,np.nan).dropna().index))\n",
    "        \n",
    "    rs_user -= set(df.loc[user].replace(0,np.nan).dropna().index)#除去自己买了的\n",
    "    rs_users[user] = rs_user\n",
    "print(\"基于用户推荐\")\n",
    "rs_users"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
